package com.arnojin.oschina.api.web;

import java.text.SimpleDateFormat;
import java.util.*;

import com.arnojin.oschina.api.job.Job1;
import com.arnojin.oschina.api.model.*;
import com.arnojin.oschina.api.repository.ProjectConfirmedRepository;
import com.arnojin.oschina.api.repository.ProjectDetailRepository;
import com.arnojin.oschina.api.repository.SkillRepository;
import com.arnojin.oschina.api.util.OkHttpUtil;
import com.arnojin.oschina.api.util.SpringUtil;
import com.google.gson.*;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.ResponseBody;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;

/**
 * Project Controller
 *
 * @author arno
 * @date 2021/01/26 17:52
 */
@Controller
@RequestMapping(path = "/project")
@Component
public class ProjectController {

    private static final Logger logger = LoggerFactory.getLogger(ProjectController.class);

    private final JobKey job1Key = JobKey.jobKey("job1", "group1");
    private final TriggerKey triggerKey1 = TriggerKey.triggerKey("trigger1", "group1");
    private Scheduler scheduler = null;
    private Trigger trigger1;

    /**
     * 获取当前 job 的运行状态
     *
     * @return ProjectResponse
     */
    @GetMapping(path = "/status")
    public @ResponseBody
    ProjectResponse status() {
        boolean success = false;
        String msg = "";

        try {
            if (scheduler != null) {
                Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey1);
                if (triggerState == Trigger.TriggerState.NORMAL) {
                    success = true;
                    msg = "ok";
                }

            }
        } catch (SchedulerException e) {
            msg = e.getMessage();
            logger.error(e.getMessage(), e);
        }

        ProjectResponse result = new ProjectResponse();
        result.success = success;
        result.message = msg;
        return result;
    }

    /**
     * 启动 job
     *
     * @return ProjectResponse
     */
    @GetMapping(path = "/start")
    public @ResponseBody
    ProjectResponse start() {
        // 每 30 分钟的第 0 秒运行
        return start(30);
    }


    /**
     * 启动 job
     *
     * @param interval 间隔的分钟数
     * @return ProjectResponse
     */
    public @ResponseBody
    ProjectResponse start(int interval) {
        boolean success = false;
        String msg = "";

        // 每 interval 分钟的第 0 秒运行
        String cronExpression = String.format("0 0/%s * * * ?", interval);

        try {
            if (trigger1 != null) {
                scheduler.resumeTrigger(trigger1.getKey());
                success = true;
                msg = "定时任务已经恢复";
            } else {
                scheduler = StdSchedulerFactory.getDefaultScheduler();
                scheduler.start();

                JobDetail job1 = newJob(Job1.class)
                        .withIdentity(job1Key)
                        .storeDurably()
                        .build();
                trigger1 = newTrigger()
                        .withIdentity(triggerKey1)
                        .withSchedule(cronSchedule(cronExpression))
                        .forJob(job1)
                        .build();

                success = true;
                scheduler.scheduleJob(job1, trigger1);
                msg = String.format("定时任务已经启动，每 %s 分钟运行一次。", interval);
            }
        } catch (SchedulerException e) {
            msg = e.getMessage();
            logger.error(msg, e);
        }

        logger.info(msg);
        ProjectResponse result = new ProjectResponse();
        result.success = success;
        result.message = msg;
        return result;
    }

    /**
     * 停止 job
     *
     * @return ProjectResponse
     */
    @GetMapping(path = "/stop")
    public @ResponseBody
    ProjectResponse stop() {
        boolean success = false;
        String msg = "";

        try {
            if (this.scheduler != null) {
                this.scheduler.pauseTrigger(trigger1.getKey());
                success = true;
                msg = "定时任务已经停止";
                logger.info(msg);
            }
        } catch (SchedulerException e) {
            msg = e.getMessage();
            logger.error(e.getMessage(), e);
        }

        ProjectResponse result = new ProjectResponse();
        result.success = success;
        result.message = msg;
        return result;
    }

    /**
     * 更新 job 的间隔运行时间
     *
     * @param interval 间隔的分钟数
     * @return ProjectResponse
     */
    @PostMapping(path = "/update")
    public @ResponseBody
    ProjectResponse update(@RequestParam("interval") int interval) {
        boolean success = false;
        String msg = "";

        // 每 interval 分钟的第 0 秒运行
        String cronExpression = String.format("0 0/%s * * * ?", interval);

        if (this.scheduler == null) {
            return start();
        }

        try {
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
            Trigger trigger = newTrigger()
                    .withIdentity(triggerKey1)
                    .withSchedule(scheduleBuilder)
                    .build();
            this.scheduler.rescheduleJob(triggerKey1, trigger);

            success = true;
            msg = String.format("定时任务已经更新为每 %s 分钟运行一次。", interval);
        } catch (SchedulerException e) {
            msg = e.getMessage();
            logger.error(msg, e);
        }

        logger.info(msg);
        ProjectResponse result = new ProjectResponse();
        result.success = success;
        result.message = msg;
        return result;
    }

    /**
     * 获取 软件项目、悬赏任务、最新成交 的是有内容
     *
     * @return ResponseBody
     */
    @GetMapping(path = "/list", produces = "application/json;charset=UTF-8")
    @ResponseBody
    public String list() {
        // 软件项目
        ProjectDetailRepository projectDetailRepository = SpringUtil.getBean(ProjectDetailRepository.class);
        ProjectDetail project = new ProjectDetail();
        project.type = 1;
        ExampleMatcher exampleMatcherProject = ExampleMatcher.matching()
                .withMatcher("type", ExampleMatcher.GenericPropertyMatchers.exact());
        Example<ProjectDetail> exampleProject = Example.of(project, exampleMatcherProject);
        JsonElement jsonElementProject = new Gson().toJsonTree(projectDetailRepository.findAll(exampleProject));

        // 悬赏任务
        ProjectDetail reward = new ProjectDetail();
        reward.type = 2;
        ExampleMatcher exampleMatcherReward = ExampleMatcher.matching()
                .withMatcher("type", ExampleMatcher.GenericPropertyMatchers.exact());
        Example<ProjectDetail> exampleReward = Example.of(reward, exampleMatcherReward);
        JsonElement jsonElementReward = new Gson().toJsonTree(projectDetailRepository.findAll(exampleReward));

        // 最新成交
        ProjectConfirmedRepository projectConfirmedRepository = SpringUtil.getBean(ProjectConfirmedRepository.class);
        JsonElement jsonElementConfirmed = new Gson().toJsonTree(projectConfirmedRepository.findAll());

        JsonObject response = new JsonObject();
        response.add("project_list", jsonElementProject);
        response.add("reward_list", jsonElementReward);
        response.add("confirmed_list", jsonElementConfirmed);
        response.addProperty("success", true);
        response.addProperty("code", 200);
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = new Date(System.currentTimeMillis());
        response.addProperty("timestamp", formatter.format(date));

        return response.toString();
    }

    /**
     * 异步获取 OsChina 的软件项目、悬赏任务、最新成交
     *
     * @return ProjectResponse
     */
    @GetMapping(path = "/get")
    public @ResponseBody
    ProjectResponse getDataFromOsChina() {
        boolean success = true;
        String msg = "ok";

        try {
            getProjectAndRewardDataFromOsChina("1");
            getProjectAndRewardDataFromOsChina("2");
            getProjectConfirmedDataFromOsChina();
        } catch (Exception ex) {
            success = false;
            msg = ex.getMessage();
            logger.error(msg, ex);
        }

        ProjectResponse result = new ProjectResponse();
        result.success = success;
        result.message = msg;
        return result;
    }

    /**
     * 异步获取 软件项目/悬赏任务 并保存
     *
     * @param type 软件项目 1，悬赏任务 2
     */
    public void getProjectAndRewardDataFromOsChina(String type) {
        String typeName = "1".equals(type) ? "project" : "reward";
        logger.info(String.format("get %s begin.", typeName));

        String url = "https://zb.oschina.net/project/contractor-browse-project-and-reward";
        Map<String, String> queries = new HashMap<String, String>(6);
        queries.put("applicationAreas", "");
        queries.put("type", type);
        queries.put("sortBy", "30");
        queries.put("currentTime", "");
        queries.put("pageSize", "2000");
        queries.put("currentPage", "1");
        String result = OkHttpUtil.get(url, queries);

        if (result.isEmpty()) {
            logger.info(String.format("get %s fail.", typeName));
            return;
        }

        JsonElement json = JsonParser.parseString(result);
        ProjectResponse projectResponse = new Gson().fromJson(json, ProjectResponse.class);

        ArrayList<Skill> skillList = new ArrayList<>();
        List<ProjectDetail> projectDetail = projectResponse.data.data;

        projectDetail.forEach(item -> {
            if (item.skillList != null && item.skillList.size() > 0) {
                StringBuilder skills = new StringBuilder();
                int size = item.skillList.size();
                for (int i = 0; i < size; i++) {
                    // 拼接 skills 字段内容
                    String skillValue = item.skillList.get(i).value;
                    skills.append(",");
                    skills.append(skillValue);

                    // 去重添加 skill
                    Skill skill = new Skill();
                    skill.value = skillValue;
                    if (!skillList.contains(skill)) {
                        skillList.add(skill);
                    }
                }
                item.skills = skills.substring(1);
            }
        });

        // 写 project_detail 表
        ProjectDetailRepository projectDetailRepository = SpringUtil.getBean(ProjectDetailRepository.class);
        projectDetailRepository.saveAll(projectDetail);

        SkillRepository skillRepository = SpringUtil.getBean(SkillRepository.class);
        List<Skill> allSkillList = skillRepository.findAll();
        allSkillList.forEach(item -> {
            if (skillList.contains(item)) {
                skillList.remove(item);
            }
        });

        // 写 skill 表
        skillRepository.saveAll(skillList);

        logger.info(String.format("get %s success.", typeName));
    }

    /**
     * 异步获取 最新成交 并保存
     */
    public void getProjectConfirmedDataFromOsChina() {
        logger.info("get confirmed begin.");

        String url = "https://zb.oschina.net/project-contract/almost-three-month-confirmed";
        String result = OkHttpUtil.get(url, null);
        if (result.isEmpty()) {
            logger.info("get project confirmed fail.");
            return;
        }

        JsonElement json = JsonParser.parseString(result);
        ProjectConfirmedResponse projectConfirmedResponse = new Gson().fromJson(json, ProjectConfirmedResponse.class);

        // 写 confirmed 表
        List<ProjectConfirmed> confirmedList = projectConfirmedResponse.data;
        ProjectConfirmedRepository confirmedRepository = SpringUtil.getBean(ProjectConfirmedRepository.class);
        confirmedRepository.saveAll(confirmedList);

        logger.info("get project confirmed success.");
    }
}
